#ifndef REVSURF_H_
#include "revSurf.h"
#endif

#define PI (3.141592653589793)
#define INVDENSIDAD 3// es 1/Densidad

using namespace std;
using std::vector;

revSurf :: revSurf(GLuint  program, vector<float>* Points):Figure(program){
	controlPoints = new vector<float>();
	controlPoints->insert(controlPoints->end(), Points->begin(), Points->end());

	vectorPerp[0] = 0;
	vectorPerp[1] = 0;
	vectorPerp[2] = 1;

	surface = new vector<float>();
	normals = new vector<float>();
	createSurface();

	/*this->vertex_buffer_size = surface->size();
	this->normal_buffer_size = normals->size();
	this->index_buffer_size  = 2*surface->size()/3;
	this->texture_buffer_size = 2*(vertex_buffer_size/3);
	this->vertex_buffer = new GLfloat[this->vertex_buffer_size+1]; //Alloco +1 por las dudas
	this->normal_buffer = new GLfloat[this->vertex_buffer_size+1];
	this->index_buffer  = new GLuint[this->index_buffer_size+1];
	this->texture_buffer = new GLfloat[this->texture_buffer_size+1];*/


	DatosVertice datos;
	size = ((4*surface->size())/3/INVDENSIDAD);
	vertices = new DatosVertice[size];
	index_size = (((4*surface->size())/3)/INVDENSIDAD);
	indices = new unsigned int[index_size];
	uint pos=0;
	for (float t=0; t < size - largoCurva - 5 ;t+=(3*INVDENSIDAD)) {
		if (pos > size) break;
		datos.position =  glm::vec3((*surface)[t],(*surface)[t+1],(*surface)[t+2]);
		datos.normal = glm::vec3((*normals)[t], (*normals)[t+1], (*normals)[t+2]);
		datos.color =  glm::vec3(0.5,0.5,0.5);
		datos.texture = glm::vec2(0.0,0.0);
		indices[pos]=pos;
		vertices[pos++] = datos;
		datos.position = glm::vec3((*surface)[t+3*largoCurva],(*surface)[t+3*largoCurva+1],(*surface)[t+3*largoCurva+2]);
		datos.normal = glm::vec3((*normals)[t+3*largoCurva], (*normals)[t+1+3*largoCurva], (*normals)[t+2+3*largoCurva]);
		datos.color =  glm::vec3(0.5,0.5,0.5);
		datos.texture = glm::vec2(0.0,1.0);
		indices[pos]=pos;
		vertices[pos++] = datos;
		datos.position = glm::vec3((*surface)[t+3],(*surface)[t+4],(*surface)[t+5]);
		datos.normal = glm::vec3((*normals)[t+3], (*normals)[t+4], (*normals)[t+5]);
		datos.color =  glm::vec3(0.5,0.5,0.5);
		datos.texture = glm::vec2(1.0,1.0);
		indices[pos]=pos;
		vertices[pos++] = datos;
		datos.position = glm::vec3((*surface)[t+3*largoCurva+3],(*surface)[t+3*largoCurva+4],(*surface)[t+3*largoCurva+5]);
		datos.normal = glm::vec3((*normals)[t+3*largoCurva+3], (*normals)[t+4+3*largoCurva], (*normals)[t+5+3*largoCurva]);
		datos.color =  glm::vec3(0.5,0.5,0.5);
		datos.texture = glm::vec2(1.0,0.0);
		indices[pos]=pos;
		vertices[pos++] = datos;
	}

	/*uint cont = 0;
	//Generamos el vector de indice.
	for(uint i = 0; i<index_size ; i++,cont+=6) {
		if ((cont+5) > index_size) break;
		if((i+1)%(largoCurva/3) != 0){
			indices[cont]   = i;
			indices[cont+1] = i+largoCurva/3;
			indices[cont+2] = i+1;

			indices[cont+3] = i+1;
			indices[cont+4] = i+largoCurva/3;
			indices[cont+5] = (i+1)+largoCurva/3;
			//Generamos la matriz de texturas
			if ( ((i+1)+largoCurva/3) < size ) {
				vertices[i].texture = glm::vec2(0, 0); //A
				vertices[i+largoCurva/3].texture = glm::vec2(0, 1); //B
				vertices[i+1].texture = glm::vec2(0, 1); //C
				vertices[(i+1)+largoCurva/3].texture = glm::vec2(1, 0); //D
			}
		} else {
			cont-=6;
		}
	}*/

	glGenBuffers(1, &vertex_buffer_object);
	glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object);
	glGenBuffers(1, &index_buffer_object);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_object);

	glBufferData(GL_ARRAY_BUFFER, size * sizeof(DatosVertice), vertices, GL_STATIC_DRAW);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, index_size * sizeof(unsigned int), indices, GL_STATIC_DRAW);

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	//this->type = GL_TRIANGLES;
	this->type = GL_TRIANGLE_STRIP;
	delete (surface);
	delete (normals);
}

void revSurf :: draw(GLuint program, glm::mat4 & world_matrix) {
	glm::mat4 matriz = world_matrix * this->model_matrix;

	//Esto esta mal!
	this->with_texture = true;

		GLuint location;
		// Normal Matrix es la inversa transpuesta 3x3 de gl_ModelViewMatrix (ViewMatrix = Camara)
		// Matrix = transformacion de afuera + transformacion del objeto

		calculate_and_bind_normal_matrix(program,matriz);
		bind_model_matrix(program,matriz);

		/*glEnableClientState(GL_VERTEX_ARRAY);
		 glEnableClientState(GL_NORMAL_ARRAY);
		 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		 glEnable(GL_CULL_FACE);



		 bind_tangent_buffer("tangent");

		 glVertexPointer(3, GL_FLOAT, 0, vertex_buffer);
		 glNormalPointer(GL_FLOAT, 0, normal_buffer);
		 glTexCoordPointer(2, GL_FLOAT, 0, texture_buffer);

		 glDrawElements(GL_QUADS, index_buffer_size, GL_UNSIGNED_INT, index_buffer);

		 if (this->show_normals) {
		 draw_normals();
		 }

		 glDisableClientState(GL_VERTEX_ARRAY);
		 glDisableClientState(GL_NORMAL_ARRAY);
		 glDisableClientState(GL_TEXTURE_COORD_ARRAY);*/

		int vertex = glGetAttribLocation(program, "vertex"); //0
		int normal = glGetAttribLocation(program, "normal"); //1
		int tangent = glGetAttribLocation(program, "tangent"); //2
		int color = glGetAttribLocation(program, "color"); //3
		int UV = glGetAttribLocation(program, "UV"); //4

		glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_object);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer_object);

		glEnableVertexAttribArray(vertex);
		glVertexAttribPointer(vertex, 3, GL_FLOAT, GL_FALSE, sizeof(DatosVertice), 0);

		glEnableVertexAttribArray(normal);
		glVertexAttribPointer(normal, 3, GL_FLOAT, GL_FALSE, sizeof(DatosVertice), (void*) (3 * sizeof(float)));

		glEnableVertexAttribArray(tangent);
		glVertexAttribPointer(tangent, 3, GL_FLOAT, GL_FALSE, sizeof(DatosVertice), (void*) (6 * sizeof(float)));

		glEnableVertexAttribArray(color);
		glVertexAttribPointer(color, 3, GL_FLOAT, GL_FALSE, sizeof(DatosVertice), (void*) (9 * sizeof(float)));

		glEnableVertexAttribArray(UV);
		glVertexAttribPointer(UV, 2, GL_FLOAT, GL_FALSE, sizeof(DatosVertice), (void*) (12 * sizeof(float)));

		// TEXTURE2D
		if (with_texture) {

			glActiveTexture(GL_TEXTURE0);
			glBindTexture(GL_TEXTURE_2D, this->texture);
			location = glGetUniformLocation(program, "Texture");
			if (location >= 0) {
				glUniform1i(location, 0);
			}
			location = glGetUniformLocation(program, "with_texture");
			if (location >= 0) {
				glUniform1i(location, 1);
			}
		} else {
			location = glGetUniformLocation(program, "with_texture");
			if (location >= 0) {
				glUniform1i(location, 0);
			}
		}

		glDrawElements(this->type, index_size, GL_UNSIGNED_INT, 0);

		glDisableVertexAttribArray(vertex);
		glDisableVertexAttribArray(normal);
		glDisableVertexAttribArray(tangent);
		glDisableVertexAttribArray(color);
		glDisableVertexAttribArray(UV);

		if (with_texture) {
			glBindTexture(GL_TEXTURE_2D, 0);
		}

		glBindBuffer(GL_ARRAY_BUFFER, 0);
		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}


revSurf :: ~revSurf(){
	delete(controlPoints);
	//delete(normals); //Se borran antes
	//delete(surface); //Se borran antes
	delete (vertices);
	delete (indices);
}

void revSurf :: createSurface(){

	BezierCuadCurve curve(controlPoints,vectorPerp);
	curve.getCurve(surface);
	curve.getNormals(normals);

	float angle = (PI*DELTAREVSURF/180); // Paso el angulo a grados(deg)
	largoCurva = curve.getSize();
	for(float i = angle ; i < 360 ; i+=angle) { //Roto el grafo hasta dar una vuelta completa
		rotateCtrlPoints(angle);
		BezierCuadCurve curve(controlPoints,vectorPerp);
		curve.getCurve(surface);
		curve.getNormals(normals);
	}
}

void revSurf :: rotateCtrlPoints(float angle){
	 glm::mat4 rot = rotateY(angle);

	 glm::vec4 Position = transform(rot,vectorPerp[0],vectorPerp[1],vectorPerp[2]);
	 vectorPerp[0] = Position[0];
	 vectorPerp[1] = Position[1];
	 vectorPerp[2] = Position[2];

	 for(size_t i=0 ; i<controlPoints->size() ; i+=3){
		glm::vec4 Position = transform(rot,(*controlPoints)[i],(*controlPoints)[i+1],(*controlPoints)[i+2]);
		(*controlPoints)[i]   = Position[0];
		(*controlPoints)[i+1] = Position[1];
		(*controlPoints)[i+2] = Position[2];
	 }
}

// transforms the position using the transformation matrix
glm::vec4 revSurf :: transform(glm::mat4 transfMat,float x, float y, float z) {

	  glm::vec4 Position = glm::vec4(x,y,z,1.0f);
	  return Position*transfMat;
}

// returns the matrix to rotate arround y axis
glm::mat4 revSurf :: rotateY(float ang){

	glm::mat4  Ry = glm::mat4(glm::vec4((float)cos(ang), 0.0f, (float)-sin(ang), 0.0f),
				glm::vec4(           0.0f, 1.0f,             0.0f, 0.0f),
				glm::vec4((float)sin(ang), 0.0f,  (float)cos(ang), 0.0f),
				glm::vec4(           0.0f, 0.0f,             0.0f, 1.0f));
	return Ry;
}

